#
# $Header: /lib/mkinitramfs-ll/functions                 Exp $
# $Author: (c) 2011-6 tokiclover <tokiclover@gmail.com>  Exp $
# $License: 2-clause/new/simplified BSD                  Exp $
# $Version: 0.22.2 2016/06/06 12:33:03                   Exp $
#

:	${COLUMNS:=80}

# @FUNCTION: cut wrapper
# @ARG: <var> <var> <char> <int> <opt>
CUT() {
	eval "${1:-REPLY}=$(echo "$2" | cut -d$3 -f${4:-:} $5)"
}

# @FUNCTION: Print begin message to stdout
# @ARG: <msg>
begin() {
	local _msg="$*"
	printf "$print_eol"
	print_eol="\n"
	print_len=$((${#msg}+8+${#name}))

	local prefix="${name:+$color_fg_mag[$color_fg_blu$name$color_fg_mag]$color_rst}"
	printf "$prefix $@"
}

# @FUNCTION: Print end message to stdout
# @ARG: <ret> [<msg>]
end() {
	local ret="$1" suffix
	case "$1" in
		(0) suffix="$color_fg_blu[${color_fg_grn}ok$color_fg_blu]$color_rst";;
		(*) suffix="$color_fg_ylw[${color_fg_red}no$color_fg_ylw]$color_rst";;
	esac
	shift
	print_len=$(($COLUMNS-$print_len))
	printf "%${print_len}s" "$*"
	printf "$suffix\n"
	print_eol=
	print_len=0
	return "$ret"
}

# @FUNCTION: Colors handler
eval_colors() {
	[ -t 1 ] && yesno "${COLOR:-Yes}" || return

	local b='4' e='\e[' f='3' c
	for c in 0:blk 1:red 2:grn 3:ylw 4:blu 5:mag 6:cyn 7:wht; do
		eval color_bg_${c#*:}="'${e}1;${b}${c%:*}m'"
		eval color_fg_${c#*:}="'${e}1;${f}${c%:*}m'"
		eval color_bg_${c%:*}="'${e}${b}${c%:*}m'"
		eval color_fg_${c%:*}="'${e}${f}${c%:*}m'"
	done
	color_rst="${e}0m"
	color_bld="${e}1m"
	color_und="${e}4m"
	color_ita="${e}3m"
}

# @FUNCTION: Print message to stdout
# @ARG: <msg>
info() {
	local prefix="${name:+$color_fg_mag$name:$color_rst}"
	printf "$print_eol${color_fg_blu}INFO: $prefix $@\n"
}

# @FUNCTION: Print warn message to stdout
# @ARG: <msg>
warn() {
	local prefix="${name:+$color_fg_red$name:$color_rst}"
	printf "$print_eol${color_fg_ylw}WARN: $prefix $@\n"
}

# @FUNCTION: Print message to stderr
# @ARG: <msg>
error() {
	local prefix="${name:+$color_fg_ylw$name:$color_rst}"
	printf "$print_eol${color_fg_red}ERROR: $prefix $@\n" >&2
}

# @FUNCTION: Printing message handler
# @ARG: [-e|-i|-c <OPT>] <msg>
msg() {
	local _opt _msg _clr

	while [ $# -ge 1 ]; do
		case "$1" in
			(-e|-i) _opt="$1";;
			(-c) _opt="$1" _clr="$2"; shift;;
			(--) ;;
			(*) _msg="$*"; break;;
		esac
		shift
	done
	case "$_opt" in
		(-e) error "${_msg}";;
		(-i) info  "${_msg}";;
	esac
	$splashd && debug splashd_cmd "set message $_msg" && debug splashd_cmd "repaint"
}

# @FUNCTION: Command exec handler
# @ARG: [-d|-e|-i] <msg>
debug() {
	local _cmd _opt _ret

	while [ $# -ge 1 ]; do
		case "$1" in
			(-d|-e|-i) _opt="$1";;
			(--) ;;
			(*) _cmd="$*"; break;;
		esac
		shift
	done
	eval "$@"
	_ret=$?
	echo "[$_ret]: $_cmd" >&3

	if [ ! "$_ret" ]; then
		case $_opt in
			(-d)    die       "${_msg:-cmd: $_cmd}";;
			(-e|-i) msg $_opt "${_msg:-cmd: $_cmd}";;
		esac
	fi
	return "$_ret"
}

# @FUNCTON: Rescue SHell helper
rescue_shell() {
	if $splashd && [ "${CONSOLE#/dev/tty}" != "$CONSOLE" ]; then
		debug openvt -c${CONSOLE#/dev/tty} $SHELL -lim <$CONSOLE >$CONSOLE 2>&1
	elif check_bin setsid; then
		debug setsid $SHELL -lim <$CONSOLE >$CONSOLE 2>&1
	else
		debug $SHELL -lim <$CONSOLE >$CONSOLE 2>&1
	fi
}

# @FUNCTION: Yes or No helper
yesno() {
	case "${1:-NO}" in
	(0|[Dd][Ii][Ss][Aa][Bb][Ll][Ee]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]|[Nn][Oo])
		return 1;;
	(1|[Ee][Nn][Aa][Bb][Ll][Ee]|[Oo][Nn]|[Tt][Rr][Uu][Ee]|[Yy][Ee][Ss])
		return 0;;
	(*)
		return 2;;
	esac
}

# @FUNCTION: Binary ChecK helper
# @ARG: <bin>
check_bin() {
	local _cmd
	for _cmd; do
		type -p $_cmd 1>$NULL 2>&1 || return
	done
}

# @FUNCTION: BusyBox Applets ChecK
check_applet() {
	local _line _list=$confdir/busybox.applets
	[ -f $_list ] || busybox --list-full >$_list

	while read _line; do
		type -p ${_line##*/} >$NULL 2>&1 || ln -s /bin/busybox /$_line
	done <$_list
}

# @FUNCTION: Hook/Script execution helper
# @ARG: <hook>
dohook() {
	local line name="${1##*/}"
	set >/environ
	begin "exec: $1...\n"
	$SHELL -ace ". /environ;
		. $libdir/functions;
		. $libdir/helpers;
		exec 3>>$logfile;
		. $1;
		exit;"
	end "$?"

	if [ -f /run/env ]; then
		while read line; do
			eval "$line"
		done </run/env
		rm -f /run/env
	fi
	[ -f /run/${1##*/}.pid ] && rm -f /run/${1##*/}.pid && debug die
	exec <$CONSOLE >$CONSOLE 2>&1
}

# @FUNCTION: rmmod wrapper
# @ARG: <module(s)|group>
RMMOD() {
	if [ -f "$confdir/module-$1" ]; then
		while read _mod; do 
			debug rmmod -q $_mod
		done <$confdir/module-$1
	else
		rmmod -q $*
	fi
}

# @FUNCTION: modprobe wrapper
# @ARG: <module(s)|group>
MODPROBE() {
	if [ -f "$confdir/module-$1" ]; then
		while read _mod; do 
			debug modprobe -q $_mod
		done <$confdir/module-$1
	else
		modprobe -q $*
	fi
}

# @FUNCTION: Kernel cmdline handler
# @ARG: <arg>
get_cmdline_option() {
	local _arg _opt
	for _arg in $*; do
		for _cmd in $CMDLINE; do
			case "$_arg" in
				("$_opt") eval "$_opt=enable"; break;;
				("${_opt%%=*}") eval "$_opt" ; break;;
			esac
		done
	done
}

# @FUNCTION: Splash commands handler
# @ARG: <cmd>
splashd_cmd() {
	echo "$@" >$splash_fifo
}

# @FUNCTION: Stop splash
splashd_stop() {
	$splashd && splashd=false || return
	debug splashd_cmd "exit"
	CONSOLE=/dev/console
	exec <$CONSOLE >$CONSOLE 2>&1
}

# @FUNCTION: Read helper with Rescue Shell
# @ARG: <var>
shread() {
	local _buffer

	while read _buffer; do
		case "$_buffer" in
			([Rr][Ss][Hh]|[Ss][Hh]|[Ss][Hh][Ee][Ll][Ll]|[Rr][Ee][Ss][Cc][Uu][Ee]|[Ss][Hh])
				die;;
			(*) eval "${1:-REPLY}='$_buffer'"; break;;
		esac
	done
}

# @FUNCTION: BLocK device handler
# @ARG: <dev|(part of)uuid|(part of)label>
blk() {
	local _asw _blk
	BLK() {
		_blk=$(blkid | sed -nre "\|${1#*=}|s|(^/dev/.*):.*$|\1|p")
		if [ -z "$_blk" ]; then
			if grep -sqw "$1" /proc/partitions; then
				_blk="/dev/$1"
			fi
		fi
	}
	BLK "$1"
	
	if ! [ -n "$_blk" -a -b "$_blk" ]; then
		msg -i "Insert $1 block device"
		sleep 1
		BLK "$1"
	fi
	while ! [ -n "$_blk" -a -b "$_blk" ]; do
		msg -i "Type in a valid block device e.g. \
			[ sda5 | (PART)UUID=<uuid> | (PART)LABEL=<label> ]"
		shread _asw
		BLK "${_asw:-$1}"
	done
	eval "${2:-REPLY}='$_blk'"
}

# @FUNCTION: dm-crypt LUKS Keyfile handler
# @ARG: <file>
ldk() {
	[ -b "$1" ] && return
	if $loopback_crypt; then
		local _ld="$1"
	else
		local _ld="$(debug -d losetup -f)"
		debug -d losetup "$_ld" "$1"
		loopback_dev="$_ld $loopback_dev"
	fi
	debug cryptsetup luksOpen "$_ld" "$_fn" && loopback_key="$_fn $loopback_key"
}

# @FUNCTION: mount remdev
# @ARG: dev
mnt_rem_dev() {
	local _rem_dev="$1"
	if ! grep -sqw $mntdir /proc/mounts; then
		[ -f $confdir/remdev ] && debug MODPROBE remdev
		[ -b "$_rem_dev" ] || blk "$_rem_dev" "_rem_dev"
		debug -d mount -n -r "$_rem_dev" $mntdir
	fi
}

# @FUNCTION: Key[file/mode] handler
# @ARG: <mode:dev:file>
get_key() {
	eval set -- $(echo "$1" | sed -e 's/:/ /g')
	local _fn="${3##*/}" _fp="$3" _kd="$2" _km="$1"

	case "$_km" in
		('') keymode=none; return;;
		(ldk)
		if [ -z "$loopback_crypt" ]; then
			[ $(cryptsetup --version | sed -nre 's/.*([0-9]?)\.([0-9]?)\.([0-9]?)/\2/p') -ge 3 ] &&
				loopback_crypt=true || loopback_crypt=false
		fi;;
		(pwd) [ -n "$_kd" ] && mnt_rem_dev "$_kd";;
		(*)
		[ -n "$_kd" ] || die "device field empty"
		[ -n "$_fp" ] || die "file path field empty"
		mnt_rem_dev "$_kd"
		debug -d test -f "$mntdir/$_fp"
		;;
	esac
	case $_km in
		(gpg) $CHECK_ENV && debug -d check_bin gpg
		      [ -f $confdir/gpg ] && debug MODPROBE gpg
		      keyfile="$mntdir/$_fp" keymode=gpg    ;;
		(reg) keyfile="$mntdir/$_fp" keymode=reg    ;;
		(ldk) ldk "$mntdir/$_fp"
		      keyfile="/dev/mapper/$_fn" keymode=ldk;;
		(pwd) keymode=pwd                           ;;
		(*) die "$_km: invalid key mode"            ;;
	esac
}

# @FUNCTION: BlocK Device handler
# @ARG: <dev> <key> [<DEV>] [<group>]
get_dev() {
	debug -d test -n "$1"
	local _cut _luks _lvm _raid _name="$2"
	local DEV _dev _grp="${3:-1}" _sig _typ

	case $_grp in
		(1) _dev="${1%%:*}";;
		(*) eval set -- $(echo "$1" | sed -e 's/:/ /g')
		_typ="$1" _dev="$2" _sig="$3" _cut=-s;;
	esac

	[ -n "$luks" ] && debug CUT "_luks" "$luks" "," "$_grp" "$_cut"
	[ -n "$raid" ] && debug CUT "_raid" "$raid" "," "$_grp" "$_cut"
	[ -n "$lvm"  ] && debug CUT "_lvm"  "$lvm"  "," "$_grp" "$_cut"
	debug -d get_key "$_luks"

	[ -n "$_raid" ] && debug -d mdopen "$_raid" "DEV"
	[ -n "$_lvm" ]  && debug -d lvopen "$_dev" "$_lvm" "DEV"
	[ -n "$keymode" -a "$keymode" != "none" ] && debug -d dmopen "$_dev" "DEV"
	[ "$keymode" =  "none" ] && debug -d blk "$_dev" "DEV"

	eval "${_name:-REPLY}='${_typ:+$_typ:}$DEV${_sig:+:$_sig}'"
}

# @FUNCTION: Close dm-crypt mapping
# @ARG: <map>
dmclose() {
	local IFS="${IFS}:" 
	for _p in $@; do
		debug cryptsetup close ${_p%-*}
	done
}

# @FUNCTION: dm-crypt LUKS device|detached-header handler
# @ARG: <dev> <var>
dmcrypt() {
	local _asw _ldh=$1

	while ! debug cryptsetup isLuks "$_ldh"; do
		msg -i "Type in a valid cyphertext device e.g. \
			[ sda5 | (PART)UUID=<uuid> | (PART)LABEL=<label> ], or avalid detached header"
		shread _asw
		[ -e "${_asw:-$1}" ] || debug blk "${_asw:-$1}" "_ldh"
	done
	eval "${2:-REPLY}='$_ldh'"
}

# @FUNCTION: dm-crypt LUKS device handler
# @ARG: <map-dev+header>
dmopen() {
	$CHECK_ENV && debug -d check_bin cryptsetup
	debug MODPROBE dm-crypt

	local _dm _arg _name="$2"
	eval set -- ${1/+/ }
	local _dev="${1##*-}" _hdr="$2" _header _map="${1%-*}"
	blk "$_dev" "_dev"
	_dm=/dev/mapper/$_map
	if [ -b "$_dm" ]; then
		eval "${_name:-REPLY}='$_dm'"
		return
	fi

	[ -z "$_hdr" ] && debug dmcrypt "$_dev" "_dev" ||
	case "$_hdr" in
		(*UUID*|*LABEL*|*sd[a-z]*)
		blk "$_hdr" "_hdr"
		debug dmcrypt "$_hdr" "_header"
		;;
		(*)
		if [ -e "$mntdir/$_hdr" ]; then
			debug dmcrypt "$mntdir/$_hdr" "_header"
		elif [ -e "$_hdr" ]; then
			debug dmcrypt "$_hdr" "_header"
		else
			die "Detached header not found"
		fi
		;;
	esac
	cryptsetup_args="$(echo $cryptsetup_args | sed 's/:/ /g')"
	_arg="open $_dev $_map ${_header:+--header} $_header $cryptsetup_args"

	case "$keymode" in
		(gpg)
		mv /dev/tty /dev/bak && cp -a /dev/console /dev/tty
		local _i
		for _i in 1 2 3; do
			debug "gpg -qd \"$keyfile\" | cryptsetup $_arg --key-file=-" && break
		done
		rm /dev/tty && mv -f /dev/bak /dev/tty
		;;
		(ldk|reg)
		debug cryptsetup $_arg -d "$keyfile"
		;;
	esac
	[ -b "$_dm" ] || debug -d cryptsetup $_arg
	debug -d test -b $_dm && eval "${_name:-REPLY}='$_dm'"
}

# @FUNCTION: LVM handler
# @ARG: <vg-lv> [<map-pv>]
lvopen() {
	$CHECK_ENV && debug -d check_bin lvm
	debug MODPROBE device-mapper
	local _lv="${1/-//}"

	if ! debug lvm lvchange -ay --noudevsync --sysinit "$_lv"; then
		case "$keymode" in
			(none) die "No logical volume found";;
			([a-z]*)
			local _pv="$2" IFS="${IFS}:"
			[ -f "$mntdir/$_pv" ] && _pv="$(cat $mntdir/$_pv)"
			for _p in $_pv; do
				debug dmopen "$_p"
			done
			debug lvm vgchange -ay --noudevsync  --sysinit "${1%-*}" ||
				die "Failed to open ${1}"
			debug lvm vgscan --mknodes
			keymode=
			;;
		esac
	fi
	if [ -b "/dev/mapper/$1" ]; then
		eval "${3:-REPLY}='/dev/mapper/$1'"
	elif [ -b "/dev/$_lv" ]; then
		eval "${3:-REPLY}='/dev/$_lv'"
	else
		die "$_lv VG/LV not found"
	fi
}

# @FUNCTION: RAID handler
# @ARG: <md<n>-opt>
mdopen() {
	local _devices _dev="${1%+*}" _opt="${1#*+}" _set _uuid
	[ "${_dev/dev}" != "$_dev" ] || _dev=/dev/$_dev
	if [ -b "$_dev" ]; then
		eval "${2:-REPLY}='$_dev'"
		return
	fi

	case "$_opt" in
		([Uu][Uu][Ii][Dd]*) _uuid="$_opt"        ;;
		(/dev/sd*[1-9])  _devices="$_opt"        ;;
		(*mapper/*:*sd*[1-9])
			_devices="${_opt#*:}"
			_devices="/dev/mapper/md-${_devices#*/}"
			local _d _p
			cd /dev
			for _d in ${_opt#*:}; do
				_p="md-${_d##*/}"
				debug -d dmopen "$_p-$_d"
			done
			cd /
			keymode=;;
		(sd*[1-9])       _devices="/dev/$_opt"   ;;
		(*-*[1-9])       _devices="/dev/sd$_opt" ;;
		(asr*|ddf*|hpt*|isw*|jmicron*|lsi*|nvidia*|pdc*|sil*|via*|dos*)
		                     _set="$_opt"        ;;
	esac
	if [ -n "$_uuid" -o -n "$_devices" ]; then
		$CHECK_ENV && debug -d check_bin mdadm
		debug MODPROBE raid
	
		if [ -n "$_uuid" ]; then
			echo ARRAY $_dev $_uuid >>/etc/mdadm.conf
		else
			echo ARRAY $_dev devices="$_devices" >>/etc/mdadm.conf
		fi
		debug mdadm --assemble ${_uuid:+-u${_uuid#*=}} -c/etc/mdadm.conf $_dev
	else
		$CHECK_ENV && debug -d check_bin dmraid
		debug MODPROBE dm-raid

		local _f _s
		for _f in $(echo "$_opt" | sed 's/:/ /g'); do
			_set="$_set $(dmraid -s -c $_f)"
		done
		for _s in ${_dev##*/} $_set; do
			debug dmraid -ay -i -I $_s
		done
	fi
	debug -d test -b $_dev
	eval "${2:-REPLY}='$_dev'"
}

# @FUNCTION: (AUFS|OverlayFS+SquashFS) Squashed-dir mounter
# @ARG: <root-dir:filesystem:OPTIONS:DIR:...>
squashd() {
	local _fs _mod _msg _squashdir="${1%%:*}" _squashd
:	${_squashdir:=squash}

	eval set -- $(echo "${1#*:}" | sed -e 's/:/ /g')
	for _arg; do
		case "$1" in
			(+[Ss]|+[Ss][Yy][Ss]|+[Ss][Yy][Ss][Tt][Ee][Mm])
				_squashd="usr bin sbin $_squashd";;
			(+[Ll]|+[Ll][Oo][Cc][Aa][Ll])
				_squashd="var/cache/edb var/db var/lib/layman $_squashd";;
			(aufs|overlay) _fs="$1" ;;
			(*) _squashd="$1 $_squashd";;
		esac
		shift
	done

	mkdir -p -m 0755 "$newroot/$_squashdir"
	debug MODPROBE squashd
	cd $newroot

	begin "Setting up kernel modules"
	for _mod in ${_fs:-aufs overlay} squashfs; do
		grep -sqw $_mod /proc/filesystems || modprobe $_mod >$NULL 2>&1 ||
			case "$_mod" in
				(overlay|squashfs) { end 1 $_mod; return 1; };;
			esac
		case "$_mod" in
			(aufs|overlay) _fs="$_mod"; break;;
		esac
	done
	end "$?"

	local _DIR _dir _opt
	for _dir in $_squashd; do
		_DIR="${_squashdir#/}/${_dir#/}" _dir="${_dir#/}"
		grep -sqw $_fs:$_dir /proc/mounts && continue
		[ -f $_DIR.squashfs ] || continue
	
		case "$_fs" in
			(aufs)
				mkdir -p -m 0755 $_DIR/rw $_DIR/rr $_dir
				_opt=nodev,udba=reval,br:$_DIR/rw:$_DIR/rr
				_msg=AUFS;;
			(overlay)
				mkdir -p -m 0755 $_DIR/rw $_DIR/up $_dir $_DIR/wk
				_opt=nodev,upperdir=$_DIR/up,lowerdir=$_DIR/rr,workdir=$_DIR/wk
				_msg=OverlayFS;;
		esac

		begin "Mounting $_fs:$_dir"
		if ! grep -sqw $_DIR/rr /proc/mounts; then
			mount -t squashfs -o nodev,ro \
				$_DIR.squashfs $_DIR/rr >${NULL} 2>&1 ||
				{ end 1 SquashFS; continue; }
		fi
		eval mount -t $_fs -o $_opt $_fs:/$_dir $_dir >$NULL 2>&1
		end "$?" "$_msg"
	done
}

# @FUNCTION: mount fstab entries
# @ARG: <dir|dev>
fsmount() {
	local _fs _dev _dir _opt _x _y _z IFS="${IFS}:"
	local name=FSmount _tab=/etc/fstab
	[ -f $newroot$_tab ] || die "no $_tab file found"

	case "${1}" in
		([aA]|[aA][lL][lL])
			begin "Mounting filesystems"
			debug -d mount -a
			end "$?"
			return
		;;
	esac

	for _x in $@; do
		eval set -- $(sed -nre "s|(^[^#].*$_x[[:space:]].*$)|\1|p" $newroot$_tab)
		_fs="$3" _dev="$1" _dir="$2" _opt="$3"
		if [ -z "$_dev" ]; then
			msg -e "$_x not entry found in $_tab"
			continue
		fi
		blk "$_dev" "_dev"
		debug -d test -b $_dev
		[ -d $newroot/$_dir ] || mkdir -p $newroot/$_dir
		begin "Mounting $_dir"
		debug -d mount -t $_fs ${_opt:+-o} $_opt $_dev $newroot/$_dir
		end "$?"
	done
}

#
# vim:fenc=utf-8:ft=sh:ci:pi:sts=0:sw=4:ts=4:
#
